package com.giCompiler.business;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.sql.Date;
import java.util.ArrayList;
import java.util.List;

import org.hibernate.Session;

import com.giCompiler.dao.CodigoFonteDao;
import com.giCompiler.dao.PacoteDao;
import com.giCompiler.bean.CodigoFonte;
import com.giCompiler.bean.Pacote;
import com.giCompiler.bean.Projeto;
import com.giCompiler.exception.BusinessException;
import com.giCompiler.util.HibernateUtil;


public class CompilarProjetoBusiness {

	//Caminho do Ismael
	//private String caminhoDiretorioProjetos = "C:\\Documents and Settings\\usuario\\workspace\\giCompiler\\WebContent\\Projetos\\";
	//private String caminhoWinrar = "C:\\Arquivos de Programas\\WinRAR\\";
	
	//Caminho do Ismael - Servidor
	private String caminhoDiretorioProjetos = "C:\\Program Files\\Apache Software Foundation\\Tomcat 6.0\\webapps\\giCompiler\\Projetos\\";
	private String caminhoWinrar = "C:\\Program Files\\WinRAR\\";
	
	//Caminho do Geovane	
	//private String caminhoDiretorioProjetos = "";
	//private String caminhoWinrar = "";
		
	public String compilarBusiness(Projeto projeto) throws BusinessException{
		
		String resultadoCompilacao = "";
		System.out.println("Iniciando metodos de compilação, aguarde...");
		
		excluirDiretorioProjeto(projeto.getNome());
	
		new File(caminhoDiretorioProjetos+projeto.getNome()+"\\src").mkdir();			
		new File(caminhoDiretorioProjetos+projeto.getNome()+"\\bin").mkdir();			
		salvarArvorePacCF(projeto);
		resultadoCompilacao = compilar(projeto.getNome());
		
		if(!resultadoCompilacao.equals(""))
			return resultadoCompilacao;
		
		montarExecutador(projeto.getNome());
		criarScriptDeCompactar(projeto.getNome());
		giCompilerLog(projeto.getNome());
		
		return null;
		
	}

	//Inicia processo de criacao do diretorios e arquivos no disco rigido, pegando os pacotes raizes do projeto
	public void salvarArvorePacCF(Projeto projeto) throws BusinessException{
		
		List<Pacote> pacotes = new ArrayList<Pacote>();
		PacoteDao pacoteDao = new PacoteDao();
		String caminhoPac = "";
		
		//Pega os pacotes raizes do projeto
		pacotes = pacoteDao.pegarPacoteRaiz(projeto.getIdProjeto());
		
		Session session = HibernateUtil.getSessionFactory().openSession();
		
		for(Pacote pacote: pacotes){

			pegarPacote(projeto, pacote, caminhoPac, session);
						
		}	
		
		session.close();
	}	
	
	//Busca todos os pacotes do projeto utilizando recusividade, efetua o salvamento dos diretorios e a busca do codigos
	//fontes referentes ao pacote.
	public void pegarPacote(Projeto projeto, Pacote pacote, String caminhoPac, Session session) throws BusinessException{
		
		List<Pacote> pacotes = new ArrayList<Pacote>();
		PacoteDao pacoteDao = new PacoteDao();
		
		caminhoPac = caminhoPac+"\\"+pacote.getNome();
		
		if(pacote.getNome().equals("default"))
			salvarPacoteHD(projeto.getNome(), pacote, caminhoPac, true);
		else
			salvarPacoteHD(projeto.getNome(), pacote, caminhoPac, false);
		
		pegarCodigoFonte(projeto, pacote, caminhoPac, session);
		
		pacotes = pacoteDao.pegarPacote(pacote.getIdPacote(), projeto.getIdProjeto(), session);
		
		for(Pacote p:pacotes){
						
			pegarPacote(projeto, p, caminhoPac, session);
			
		}		
	}
	
	//Busca todos o codigos fontes referente a um determinado pacote de um projeto
	public void pegarCodigoFonte(Projeto projeto, Pacote pacote, String caminhoPac, Session session) throws BusinessException{
		
		List<CodigoFonte> cfs = new ArrayList<CodigoFonte>();
		CodigoFonteDao codigoFonteDao = new CodigoFonteDao();
		
		cfs = codigoFonteDao.pegarCodigoFontesDoPacote(pacote.getIdPacote());
		
		for(CodigoFonte cf:cfs){
			
			if(pacote.getNome().equals("default"))
				salvarCodigoFonteHD(projeto.getNome(), cf, caminhoPac, true);
			else
				salvarCodigoFonteHD(projeto.getNome(), cf, caminhoPac, false);
			
		}	
		
	}
	
	//Salva pacote no disco rigido
	public void salvarPacoteHD(String nomeProjeto, Pacote pacote, String caminhoPac, boolean ePacDefault) throws BusinessException{
		
		if(!ePacDefault){
			
			//System.out.println(caminhoDiretorioProjetos+nomeProjeto+"\\src"+caminhoPac);			
			File file = new File(caminhoDiretorioProjetos+nomeProjeto+"\\src"+caminhoPac);			
			
			if(!file.mkdir()){
				System.out.println("Error ao tentar criar os diretorios do projeto!");
				throw new BusinessException("Error ao tentar criar os diretorios do projeto!");
			}
				
		}
	}
	
	//Salva no disco rigido na pasta do projeto com a ajuda do metodo overide
	public void salvarCodigoFonteHD(String nomeProjeto, CodigoFonte codigoFonte, String caminhoPac, boolean ePacDefault) throws BusinessException{
		
		if(ePacDefault){
		
			//System.out.println(caminhoDiretorioProjetos+nomeProjeto+"\\src\\"+codigoFonte.getNome()+".java");
			File file = new File(caminhoDiretorioProjetos+nomeProjeto+"\\src\\"+codigoFonte.getNome()+".java");
			salvarCodigoFonte(nomeProjeto, codigoFonte, file);
			
		}else{
		
			//System.out.println(caminhoDiretorioProjetos+nomeProjeto+"\\src"+caminhoPac+"\\"+codigoFonte.getNome()+".java");
			File file = new File(caminhoDiretorioProjetos+nomeProjeto+"\\src"+caminhoPac+"\\"+codigoFonte.getNome()+".java");			
			salvarCodigoFonte(nomeProjeto, codigoFonte, file);
			
		}	
	}
	
	//Salva codigo-fonte no disco rigido
	private void salvarCodigoFonte(String nomeProjeto, CodigoFonte codigoFonte, File file) throws BusinessException{

		try {
			FileWriter fw = new FileWriter(file);
			PrintWriter pw = new PrintWriter(fw);
			pw.println(codigoFonte.getCodigoFonte());
			pw.flush();
			pw.close();
		} catch (IOException e) {			
			e.printStackTrace();
			System.out.println("Error ao tentar salvar o arquivo codigo fonte!");
			throw new BusinessException("Error ao tentar salvar o arquivo codigo fonte!");
		}	
			
	}	
	
	//Inicia processo de excluir todos os diretorio do projeto
	public boolean excluirDiretorioProjeto(String nomeProjeto){

		boolean sucessoDirBin = false;
		boolean sucessoDirSrc = false;
		
		File dirBin = new File(caminhoDiretorioProjetos+nomeProjeto+"\\bin");
		File dirSrc = new File(caminhoDiretorioProjetos+nomeProjeto+"\\src");
		
		sucessoDirBin = excluirArquivosESubDiretorios(dirBin);
		sucessoDirSrc = excluirArquivosESubDiretorios(dirSrc);
		
		if(sucessoDirBin == true && sucessoDirSrc == true)
			return true; //Excluiu com sucesso
		else
			return false; //Error ao excluir		
	
	}
	
	//Exclui todos os arquivos e subdiretorios de um determinado diretorio
	public boolean excluirArquivosESubDiretorios(File dirProjeto){
				
		if (dirProjeto.isDirectory()) {
			String[] filho = dirProjeto.list();
		    for (int i=0; i<filho.length; i++) {
		        boolean sucesso = excluirArquivosESubDiretorios(new File(dirProjeto, filho[i]));
		        if (!sucesso) {
		            return false;
		        }
		    }
		}

		//O diretorio esta limpo então e deletado
		return dirProjeto.delete();
		
	}
	
	//Este metodo nao e mais utilizado
	//Le log de erros da compilacao
	public String leError(String nomeProjeto) throws BusinessException{
		
		String caminhoArquivoError =  caminhoDiretorioProjetos+nomeProjeto+"\\"+"error.log";
		String error = "";
		String linha = "";
				  
		File file = new File(caminhoArquivoError);
		
		try {
			FileReader fr = new FileReader(file);
			BufferedReader br = new BufferedReader(fr);   
			  
			while(null != (linha = br.readLine())){
				error += linha+"\n"; 
			}
			br.close();
			fr.close();
		   
		} catch (FileNotFoundException e) {
			e.printStackTrace(); 
			throw new BusinessException("Error ao tentar ao tentar ler o arquivo de error!");
		} catch (IOException e) {
			e.printStackTrace();
			throw new BusinessException("Error ao tentar ao tentar ler o arquivo de error!");
		}
		
		return error;
	  
	}
	
	//Compila projeto
	public String compilar(String nomeProjeto) throws BusinessException{

		String resultado = "";
		String linha = null; 

		String comando = "javac -classpath \""+caminhoDiretorioProjetos+nomeProjeto+"\\src\" -d \""+caminhoDiretorioProjetos+nomeProjeto+"\\bin\" \""+caminhoDiretorioProjetos+nomeProjeto+"\\src\\*.java\"";
		//System.out.println(comando);
		
		Runtime rt = Runtime.getRuntime();
		
		try {
			
			//Executa comando de compilacao
			Process process = rt.exec(comando);
			
			//Le o erro gerando pelo comando
			BufferedReader errorOutput = new BufferedReader (new InputStreamReader(process.getErrorStream())); 
			
		    while((linha = errorOutput.readLine()) != null){
		    	System.out.println (linha); 
		    	resultado += linha;
		    }
			
		} catch (IOException e) {
			e.printStackTrace();
		}		
		
		if(resultado == null)
			resultado = verificarPastaBin(nomeProjeto);
		else
			salvarLogError(resultado, nomeProjeto);
		
		
	    return resultado;
	}
	
	//Verifica se foi gerado os arquivos class na pasta bin
	public String verificarPastaBin(String nomeProjeto) throws BusinessException{
		
		File dirBin = new File(caminhoDiretorioProjetos+nomeProjeto+"\\bin");
		
		String[] dirArq = dirBin.list();
		
		if(dirArq.length == 0){
			salvarLogError("Error Fatal na Compilacao!",  nomeProjeto);
			return "Error fatal de compilacao - Verifique se seu projeto esta sendo construido de forma correta!";
		}else
			return "";
				
	}
	
	//Salva erro gerado pela compilacao no arquivo error.log localizado no projeto
	public void salvarLogError(String error, String nomeProjeto) throws BusinessException{
		
		//System.out.println("\n\n"+caminhoDiretorioProjetos+nomeProjeto+"\\error.log");
		File file = new File(caminhoDiretorioProjetos+nomeProjeto+"\\error.log");
		
		try {
			FileWriter fw = new FileWriter(file);
			PrintWriter pw = new PrintWriter(fw);
			pw.println(error);
			pw.flush();
			pw.close();
		} catch (IOException e) {			
			e.printStackTrace();
			System.out.println("Error ao tentar salvar o error de compilacao!");
			throw new BusinessException("Error ao tentar salvar o error de compilacao!");
		}	
				
	}
	
	//Cria script que ao iniciar executa a classe main, mostrando seu resultado no prompt
	public void montarExecutador(String nomeProjeto) throws BusinessException{
		
		String caminhoArquivoBat = caminhoDiretorioProjetos+nomeProjeto+"\\"+nomeProjeto+"Exec.bat";
		
		String batScript = "@echo off \n cd bin \n java "+nomeProjeto+"Main \nPAUSE";
		
		File file = new File(caminhoArquivoBat);
		
		try {
			FileWriter fw = new FileWriter(file);
			PrintWriter pw = new PrintWriter(fw);
			pw.println(batScript);
			pw.flush();
			pw.close();
		} catch (IOException e) {
			e.printStackTrace();
			throw new BusinessException("Error ao tentar criar o executador");
		}
		
	}
	
	//Cria script de compactacao do projeto
	public void criarScriptDeCompactar(String nomeProjeto) throws BusinessException{
		
		String caminhoArquivoBat = caminhoDiretorioProjetos+nomeProjeto+"\\"+nomeProjeto+"Compactador.bat";
		
		String batScript = "set path=\""+caminhoWinrar+"\";%path% \n"+
		   				   "cd "+caminhoDiretorioProjetos+nomeProjeto+" \n"+
		                   "rar a "+nomeProjeto+".zip src bin "+nomeProjeto+"Exec.bat giCompilerReadme.txt";
		

		File file = new File(caminhoArquivoBat);
		
		try {
			FileWriter fw = new FileWriter(file);
			PrintWriter pw = new PrintWriter(fw);
			pw.println(batScript);
			pw.flush();
			pw.close();
		} catch (IOException e) {
			e.printStackTrace();
			throw new BusinessException("Error ao tentar criar o compactador!");
		}

		executarScriptCompactador(caminhoArquivoBat);
		
		file.delete();
	}
	
	//Executa script de compactacao do projeto
	public void executarScriptCompactador(String caminhoArquivoBat) throws BusinessException{
		
		InputStream inputStream;
		String linha = "";
		ProcessBuilder builder = new ProcessBuilder(new String[] {caminhoArquivoBat});
			
		try{
			
			Process process = builder.start();

			inputStream = process.getInputStream();   
	
	        InputStreamReader isr = new InputStreamReader(inputStream);
	        BufferedReader br = new BufferedReader(isr);
	        
	        while ((linha = br.readLine()) != null) {
	            //System.out.println(linha);
	        }            
			
	        process.waitFor();   
           
		} catch (Exception e) {   
            e.printStackTrace();   
            throw new BusinessException("Error ao tentar executar o compactador!");
        }		
	}
	
	//Gera arquivo de log da compilacao
	public void giCompilerLog(String nomeProjeto) throws BusinessException {
		
		File file = new File(caminhoDiretorioProjetos+nomeProjeto+"\\giCompilerReadme.txt");
		Date date = new Date (System.currentTimeMillis());
		
		try {
			FileWriter fw = new FileWriter(file);
			PrintWriter pw = new PrintWriter(fw);
			pw.println("Este arquivo foi gerado pelo giCompiler no dia "+date.toString());
			pw.flush();
			pw.close();
		} catch (IOException e) {
			e.printStackTrace();
			throw new BusinessException("Error ao tentar criar o giCompilerLog!");
		}
		
	}
	
}